<template>
  <div>
    <div style="margin-right: 20px; display: inline-block">切换难度:</div>
    <el-radio :value="level" :label="1" @change="selectLevel(1)">初级</el-radio>
    <el-radio :value="level" :label="2" @change="selectLevel(2)">中级</el-radio>
    <el-radio :value="level" :label="3" @change="selectLevel(3)">高级</el-radio>
    <el-button
      style="margin-bottom: 20px; margin-left: 20px"
      @click="restartGame"
      >重新开始</el-button
    >
    <br>
    <div class="sl-container">
      <div class="game-header">
        <div class="time-area left">
          <img
            v-for="(item, index) in flagCountDownArr"
            :key="index"
            :src="imgArr[item]"
          />
        </div>
        <div class="time-area right">
          <img
            v-for="(item, index) in timeCountArr"
            :key="index"
            :src="imgArr[item]"
          />
        </div>
        <div class="game-face">
          <img v-if="gameover !== 2" :src="imgArr[10]" alt="" />
          <img v-else :src="imgArr[11]" alt="" />
        </div>
      </div>
      <div class="boom-area">
        <div
          class="boom-line"
          v-for="ls in list"
          :key="ls.index"
          :class="{ gameover: gameover !== 0 }"
        >
          <div
            v-for="item in ls.child"
            :key="item.index"
            :class="{
              'boom-item': true,
              one: item.boomCount === 1 && item.isShow,
              two: item.boomCount === 2 && item.isShow,
              three: item.boomCount === 3 && item.isShow,
              four: item.boomCount === 4 && item.isShow,
              five: item.boomCount === 5 && item.isShow,
              six: item.boomCount === 6 && item.isShow,
              zero: item.boomCount === 0 && item.isShow && !item.isBoom,
              boom: item.isBoom && !item.isBoomPoint && gameover === 2,
              flag: item.flagBoom && gameover !== 2,
              'boom-point': item.isBoom && item.isBoomPoint && gameover === 2,
              'wrong-boom': !item.isBoom && item.flagBoom && gameover === 2,
            }"
            @click="tickBoom(ls.index, item.index)"
            @contextmenu.prevent="flagBoom(ls.index, item.index)"
          >
            {{
              item.isShow &&
              !item.isBoom &&
              item.boomCount > 0 &&
              !item.flagBoom
                ? item.boomCount
                : ""
            }}
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import { getRndInteger } from '../../../util/utils'
const d1 = require('../../../assets/d1.gif')
const d2 = require('../../../assets/d2.gif')
const d3 = require('../../../assets/d3.gif')
const d4 = require('../../../assets/d4.gif')
const d5 = require('../../../assets/d5.gif')
const d6 = require('../../../assets/d6.gif')
const d7 = require('../../../assets/d7.gif')
const d8 = require('../../../assets/d8.gif')
const d9 = require('../../../assets/d9.gif')
const d0 = require('../../../assets/d0.gif')
const faceNormal = require('../../../assets/face_normal.gif')
const faceFail = require('../../../assets/face_fail.gif')
const checkMap = new Map()
const totalMap = new Map()
export default {
  name: 'Boom',
  data () {
    return {
      list: [],
      level: 3, // 初级 9*9 10个雷  中级 16*16 40个雷  高级30*16 99个雷
      levelOption: [
        { value: 1, label: '初级', x: 9, y: 9, b: 10 },
        { value: 2, label: '中级', x: 16, y: 16, b: 40 },
        { value: 3, label: '高级', x: 30, y: 16, b: 99 }
      ],
      gameover: 0, // 1：赢了 2：输了 0：局中
      flagCount: 0, // 标记旗子的位置数
      count: 0, // 已经显示的位置数
      timer: null,
      timeCount: 0,
      flagCountDown: 99,
      imgArr: [d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, faceNormal, faceFail]
    }
  },
  computed: {
    total () {
      const option = this.levelOption[this.level - 1]
      return option.x * option.y
    },
    boomTotal () {
      return this.levelOption[this.level - 1].b
    },
    timeCountArr () {
      if (this.timeCount <= 999) {
        const result = []
        result.push(Math.floor(this.timeCount / 100))
        result.push(Math.floor((this.timeCount % 100) / 10))
        result.push((this.timeCount % 100) % 10)
        return result
      } else {
        return this.timeCountArr
      }
    },
    flagCountDownArr () {
      if (this.flagCountDown >= 0) {
        const result = []
        result.push(Math.floor(this.flagCountDown / 100))
        result.push(Math.floor((this.flagCountDown % 100) / 10))
        result.push((this.flagCountDown % 100) % 10)
        return result
      } else {
        return this.flagCountDownArr
      }
    }
  },
  methods: {
    restartGame () {
      const that = this
      this.$confirm('确定要重新开局吗？', '提示', {
        confirmButtonText: '确认',
        cancelButtonText: '取消'
      })
        .then(() => {
          that.generateList()
        })
        .catch(action => {
        })
    },
    selectLevel (level) {
      const that = this
      if (this.level !== level) {
        this.$confirm('确定要切换难度并重新开局吗？', '提示', {
          confirmButtonText: '确认',
          cancelButtonText: '取消'
        })
          .then(() => {
            that.level = level
            that.generateList()
          })
          .catch(action => {
          })
      }
    },
    generateList () {
      let result = []
      checkMap.clear()
      totalMap.clear()
      // 如果有计时器，清除掉
      if (this.timer) {
        clearInterval(this.timer)
        this.timer = null
      }
      const levelObj = this.levelOption[this.level - 1]
      // 初始化参数
      this.flagCountDown = levelObj.b
      this.timeCount = 0
      this.gameover = 0
      this.flagCount = 0

      // 重新生成扫雷区域的二维数组
      for (let i = 0; i < levelObj.y; i++) {
        const arr = {
          index: i,
          child: []
        }
        for (let j = 0; j < levelObj.x; j++) {
          arr.child.push({
            index: j,
            isBoom: false,
            boomCount: 0, // 周围雷的个数
            isShow: false, // 是否展示内容
            isBoomPoint: false, // 是否引爆点
            flagBoom: false // 是否被标记为雷
          })
        }
        result.push(arr)
      }
      result = this.generateBoom(result, levelObj.b, levelObj.x, levelObj.y)
      result = this.calcBoomCount(result, levelObj.x, levelObj.y)
      this.list = result
    },
    generateBoom (list, boomCount, x, y) {
      const newList = JSON.parse(JSON.stringify(list))
      const result = []
      for (let i = 0; i < boomCount; i++) {
        // 生成一个0~x*y-1的随机数
        let flag = true
        while (flag) {
          const random = getRndInteger(0, x * y - 1)
          if (result.indexOf(random) < 0) {
            flag = false
            result.push(random)
          }
        }
      }
      for (let j = 0; j < boomCount; j++) {
        const xl = Math.floor(result[j] / x)
        const yl = result[j] % x
        newList[xl].child[yl].isBoom = true
      }
      return newList
    },
    calcBoomCount (list, x, y) {
      const newList = JSON.parse(JSON.stringify(list))
      for (let i = 0, len = newList.length; i < len; i++) {
        for (let j = 0, jLen = newList[i].child.length; j < jLen; j++) {
          const item = newList[i].child[j]
          if (!item.isBoom) {
            // 自身不是雷
            let count = 0
            // 上
            if (i - 1 >= 0 && newList[i - 1].child[j].isBoom) {
              count++
            }
            // 左上
            if (
              i - 1 >= 0 &&
              j - 1 >= 0 &&
              newList[i - 1].child[j - 1].isBoom
            ) {
              count++
            }
            // 右上
            if (i - 1 >= 0 && j + 1 < x && newList[i - 1].child[j + 1].isBoom) {
              count++
            }
            // 下
            if (i + 1 < y && newList[i + 1].child[j].isBoom) {
              count++
            }
            // 左下
            if (i + 1 < y && j - 1 >= 0 && newList[i + 1].child[j - 1].isBoom) {
              count++
            }
            // 右下
            if (i + 1 < y && j + 1 < x && newList[i + 1].child[j + 1].isBoom) {
              count++
            }
            // 左
            if (j - 1 >= 0 && newList[i].child[j - 1].isBoom) {
              count++
            }
            // 右
            if (j + 1 < x && newList[i].child[j + 1].isBoom) {
              count++
            }
            item.boomCount = count
          }
        }
      }
      return newList
    },
    tickBoom (x, y) {
      // 点击方格
      if (!this.checkBeforeClick(x, y, 'left')) {
        return
      }
      const levelOption = this.levelOption[this.level - 1]
      const xCount = levelOption.x
      const yCount = levelOption.y
      this.list[x].child[y].isShow = true
      totalMap.set(`${x}_${y}`, 1)
      if (this.list[x].child[y].isBoom) {
        // 如果点中的是炸弹，则游戏结束
        this.list[x].child[y].isBoomPoint = true
        this.gameover = 2
        // 清除定时器
        clearInterval(this.timer)
        this.timer = null
        this.$message.error('You Lose！！！')
      } else if (this.list[x].child[y].boomCount === 0) {
        this.findEmptyAround(this.list, x, y, xCount, yCount)
        for (const key of checkMap.keys()) {
          if (!totalMap.has(key)) {
            totalMap.set(key, 1)
          }
          const keyArr = key.split('_')
          this.list[keyArr[0]].child[keyArr[1]].isShow = true
        }
      }
      if (this.checkWin()) {
        // 如果赢了
        // 清除定时器
        clearInterval(this.timer)
        this.timer = null
        this.gameover = 1
        this.$message.success('You Win！！！')
      }
    },
    findEmptyAround (list, x, y, xCount, yCount) {
      let goOnMap = new Map()
      const newList = JSON.parse(JSON.stringify(list))
      const arr = this.getAroundPosition(x, y, xCount, yCount)
      arr.forEach((v) => {
        if (checkMap.has(`${v.x}_${v.y}`)) {
          return
        }
        const item = newList[v.x].child[v.y]
        if (
          item.boomCount === 0 &&
          !item.isShow &&
          !item.isBoom &&
          !item.flagBoom
        ) {
          if (!goOnMap.has(`${v.x}_${v.y}`)) {
            goOnMap.set(`${v.x}_${v.y}`, v)
          }
        }
        checkMap.set(`${v.x}_${v.y}`, item.boomCount)
      })
      while (goOnMap.size > 0) {
        const tempMap = new Map()
        for (const value of goOnMap.values()) {
          const pArr = this.getAroundPosition(value.x, value.y, xCount, yCount)
          pArr.forEach((v) => {
            if (checkMap.has(`${v.x}_${v.y}`)) {
              return
            }
            const vItem = newList[v.x].child[v.y]
            if (
              vItem.boomCount === 0 &&
              !vItem.isShow &&
              !vItem.isBoom &&
              !vItem.flagBoom
            ) {
              if (!tempMap.has(`${v.x}_${v.y}`)) {
                tempMap.set(`${v.x}_${v.y}`, v)
              }
            }
            checkMap.set(`${v.x}_${v.y}`, vItem.boomCount)
          })
        }
        goOnMap = new Map(tempMap)
      }
    },
    getAroundPosition (x, y, xCount, yCount) {
      const result = []
      // 上
      if (x - 1 >= 0) {
        result.push({
          x: x - 1,
          y: y
        })
      }
      // 下
      if (x + 1 < yCount) {
        result.push({
          x: x + 1,
          y: y
        })
      }
      // 左
      if (y - 1 >= 0) {
        result.push({
          x: x,
          y: y - 1
        })
      }
      // 右
      if (y + 1 < xCount) {
        result.push({
          x: x,
          y: y + 1
        })
      }
      // 左上
      if (x - 1 >= 0 && y - 1 >= 0) {
        result.push({
          x: x - 1,
          y: y - 1
        })
      }
      // 右上
      if (x - 1 >= 0 && y + 1 < xCount) {
        result.push({
          x: x - 1,
          y: y + 1
        })
      }
      // 左下
      if (x + 1 < yCount && y - 1 >= 0) {
        result.push({
          x: x + 1,
          y: y - 1
        })
      }
      // 右下
      if (x + 1 < yCount && y + 1 < xCount) {
        result.push({
          x: x + 1,
          y: y + 1
        })
      }
      return result
    },
    flagBoom (x, y) {
      if (!this.checkBeforeClick(x, y, 'right')) {
        return
      }
      if (this.list[x].child[y].flagBoom === true) {
        this.list[x].child[y].flagBoom = false
        this.flagCount = this.flagCount - 1
        this.flagCountDown = this.flagCountDown + 1
        return
      }
      if (this.boomTotal <= this.flagCount) {
        this.$message.error('标记数已达到总的雷数')
        return
      }
      this.list[x].child[y].flagBoom = true
      this.flagCount = this.flagCount + 1
      this.flagCountDown = this.flagCountDown - 1
      if (this.checkWin()) {
        // 如果赢了
        // 清除定时器
        clearInterval(this.timer)
        this.timer = null
        this.gameover = 1
        this.$message.success('You Win！！！')
      }
    },
    checkBeforeClick (x, y, type) {
      const that = this
      const item = this.list[x].child[y]
      // 比赛没有结束，且timer没有被赋值，则开启倒计时
      if (!this.timer && this.gameover === 0) {
        this.timer = setInterval(function () {
          const newTime = that.timeCount + 1
          that.timeCount = newTime
          if (newTime === 999) {
            clearInterval(that.timer)
            that.timer = null
          }
        }, 1000)
      }
      let bool = true
      if (this.gameover !== 0) {
        // console.log('比赛结束不能点')
        bool = false
      }
      if (item.isShow) {
        // console.log('已经显示的不能点')
        bool = false
      }
      if (item.flagBoom && type === 'left') {
        // console.log('已经被标记的不能点')
        bool = false
      }
      return bool
    },
    checkWin () {
      return this.flagCount + totalMap.size === this.total
    }
  },
  mounted () {
    this.generateList()
  }
}
</script>
<style scoped>
.sl-container {
  background: #c0c0c0;
  display: inline-block;
  padding: 7px;
}
.game-header {
  border: 2px solid #808080;
  border-right-color: #fff;
  border-bottom-color: #fff;
  padding: 5px 6px;
  margin-bottom: 8px;
  overflow: hidden;
  text-align: center;
}
.time-area {
  font-size: 0;
}
.time-area.left {
  float: left;
}
.time-area.right {
  float: right;
}
.game-face {
  display: inline-block;
  border: 2px solid #808080;
  border-left-color: #fff;
  border-top-color: #fff;
  height: 20px;
  width: 20px;
  font-size: 0;
}
.boom-area {
  display: inline-block;
  border: 3px solid #808080;
  border-right-color: #fff;
  border-bottom-color: #fff;
}
.boom-line {
  height: 22px;
}
.boom-item {
  display: inline-block;
  width: 22px;
  height: 22px;
  background-image: url(../../../assets/boom-bg.png);
  background-size: 22px;
  background-repeat: no-repeat;
  padding-left: 5px;
  line-height: 20px;
  color: #0000ff;
  box-sizing: border-box;
  font-weight: 600;
  vertical-align: top;
  cursor: pointer;
}
.boom-line:not(.gameover) .boom-item:hover {
  width: 22px;
  height: 22px;
  background: #c0c0c0;
  border-top: 1px solid #858585;
  border-left: 1px solid #858585;
}
.one {
  color: #0000ff;
  background: #c0c0c0;
  border-top: 1px solid #858585;
  border-left: 1px solid #858585;
}
.two {
  color: #018001;
  background: #c0c0c0;
  border-top: 1px solid #858585;
  border-left: 1px solid #858585;
}
.three {
  color: #fe0101;
  background: #c0c0c0;
  border-top: 1px solid #858585;
  border-left: 1px solid #858585;
}
.four {
  color: #000080;
  background: #c0c0c0;
  border-top: 1px solid #858585;
  border-left: 1px solid #858585;
}
.five {
  color: #800101;
  background: #c0c0c0;
  border-top: 1px solid #858585;
  border-left: 1px solid #858585;
}
.six {
  color: #000;
  background: #c0c0c0;
  border-top: 1px solid #858585;
  border-left: 1px solid #858585;
}
.zero {
  background: #c0c0c0;
  border-top: 1px solid #858585;
  border-left: 1px solid #858585;
}
.boom {
  background-image: url(../../../assets/boom.png) !important;
}
.boom:hover {
  background-size: 22px !important;
  border: none !important;
}
.flag {
  background-image: url(../../../assets/flag.png) !important;
  background-size: 22px;
  border: none !important;
}
.flag:hover {
  background: none;
  background-image: url(../../../assets/flag.png) !important;
  background-size: 22px !important;
  border: none !important;
}
.boom-point {
  background-size: 22px;
  background-image: url(../../../assets/boom-point.png) !important;
  border: none !important;
}
.boom-point:hover {
  background-size: 22px !important;
  border: none !important;
}
.wrong-boom {
  background-size: 22px;
  background-image: url(../../../assets/wrong-boom.png) !important;
  border: none !important;
}
.wrong-boom:hover {
  background-size: 22px !important;
  border: none !important;
}
</style>
